// GameScreen.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.ComponentModel;
using System.Linq;
using System.Media;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

using Supremacy.Annotations;
using Supremacy.Client.Events;
using Supremacy.Client.Views;
using Supremacy.Diplomacy;
using Supremacy.Game;
using Supremacy.Universe;

namespace Supremacy.Client
{
    public class GameScreenBase : Control
    {
        private readonly IUnityContainer _container;
        private readonly IRegionManager _regionManager;
        private readonly IPlayerOrderService _playerOrderService;

        #region Static Members
        public static readonly DependencyProperty ClientContextProperty;
        public static readonly RoutedCommand DebugIncreaseTechLevelsCommand;
        public static readonly RoutedCommand DebugShowMapCommand;
        public static readonly RoutedCommand DebugAddCreditsCommand;
        public static readonly RoutedEvent ChatMessageReceivedEvent;

        static GameScreenBase()
        {
            ClientContextProperty = GameScreenViewBase.ClientContextProperty.AddOwner(typeof(GameScreenBase));

            SnapsToDevicePixelsProperty.OverrideMetadata(
                typeof(GameScreenBase),
                new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));

            IsTabStopProperty.OverrideMetadata(
                typeof(GameScreenBase),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));

            DebugIncreaseTechLevelsCommand = new RoutedCommand(
                "DebugIncreaseTechLevels",
                typeof(GameScreenBase));

            DebugShowMapCommand = new RoutedCommand(
                "DebugShowMap",
                typeof(GameScreenBase));

            DebugAddCreditsCommand = new RoutedCommand(
                "DebugAddCredits",
                typeof(GameScreenBase));

            ChatMessageReceivedEvent = EventManager.RegisterRoutedEvent(
                "ChatMessageReceived",
                RoutingStrategy.Direct,
                typeof(RoutedEventHandler),
                typeof(GameScreenBase));
        }

        #region Public and Protected Methods
        public static IClientContext GetClientContext(DependencyObject element)
        {
            if (element == null)
                return null;
            return element.GetValue(ClientContextProperty) as IClientContext;
        }

        public static void SetClientContext(DependencyObject element, IClientContext value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(ClientContextProperty, value);
        }
        #endregion

        protected GameScreenBase([NotNull] IUnityContainer container)
        {
            if (container == null)
                throw new ArgumentNullException("container");

            _container = container;
            _regionManager = _container.Resolve<IRegionManager>();
            _playerOrderService = _container.Resolve<IPlayerOrderService>();

            this.ClientContext = _container.Resolve<IClientContext>();
        }
        #endregion

        public IClientContext ClientContext
        {
            get { return GetValue(ClientContextProperty) as IClientContext; }
            set { SetValue(ClientContextProperty, value); }
        }

        protected IPlayerOrderService PlayerOrderService
        {
            get { return _playerOrderService; }
        }

        protected IRegionManager RegionManager
        {
            get { return _regionManager; }
        }

        protected void PauseAnimations()
        {
            foreach (var animationsHost in this.FindVisualDescendantsByType<DependencyObject>().OfType<IAnimationsHost>())
            {
                try { animationsHost.PauseAnimations(); }
                catch { }
            }
        }

        protected void ResumeAnimations()
        {
            foreach (var animationsHost in this.FindVisualDescendantsByType<DependencyObject>().OfType<IAnimationsHost>())
            {
                try { animationsHost.ResumeAnimations(); }
                catch { }
            }
        }
    }

    public abstract class GameScreen<TPresentationModel> : GameScreenBase, IGameScreenView<TPresentationModel>
    {
        private bool _isActive;

        protected GameScreen([NotNull] IUnityContainer container) : base(container)
        {
            InputBindings.Add(
                new KeyBinding(
                    DebugIncreaseTechLevelsCommand,
                    Key.F11,
                    ModifierKeys.None));

            InputBindings.Add(
                new KeyBinding(
                    DebugAddCreditsCommand,
                    Key.F10,
                    ModifierKeys.None));

            InputBindings.Add(
                new KeyBinding(
                    DebugShowMapCommand,
                    Key.F12,
                    ModifierKeys.None));

            CommandBindings.Add(
                new CommandBinding(
                    DebugShowMapCommand,
                    DebugShowMapCommand_Executed));

            CommandBindings.Add(
                new CommandBinding(
                    DebugAddCreditsCommand,
                    DebugAddCreditsCommand_Executed));

            Loaded += GameScreen_Loaded;
            Unloaded += GameScreen_Unloaded;
            IsVisibleChanged += GameScreen_IsVisibleChanged;
            IsActiveChanged += HandleIsActiveChanged;

            ClientEvents.ScreenRefreshRequired.Subscribe(e => RefreshScreen());
        }

        private void HandleIsActiveChanged(object sender, EventArgs args)
        {
            var region = RegionManager.Regions[ClientRegions.GameScreens];
            if (!region.Views.Contains(this))
                return;

            if (_isActive)
                region.Activate(this);
            else
                region.Deactivate(this);
        }

        public void BringDescendantIntoView(DependencyObject descendant)
        {
            var target = descendant;
            while ((target != null) && (target != this))
            {
                if (target.ReadLocalValue(Selector.IsSelectedProperty) != DependencyProperty.UnsetValue)
                    Selector.SetIsSelected(target, true);
                target = WpfHelper.GetParent(target as FrameworkElement);
            }
            var descendantElement = descendant as FrameworkElement;
            if (descendantElement != null)
                descendantElement.BringIntoView();
        }

        private void GameScreen_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (IsVisible)
                OnShowCore();
            else
                OnHideCore();
        }

        private void OnHideCore()
        {
            PauseAnimations();
            OnHide();
        }

        private void OnShowCore()
        {
            ResumeAnimations();
            OnShow();
            //if (Settings.Default.EnableScreenTransitions)
            //    BeginAnimation(OpacityProperty, _fadeInAnimation);
        }

        protected virtual void OnHide() {}

        protected virtual void OnShow() {}

        private void StopDescendantAnimations()
        {
            foreach (var animationsHost in this.FindVisualDescendantsByType<DependencyObject>().OfType<IAnimationsHost>())
            {
                try { animationsHost.StopAnimations(); }
                catch {}
            }
        }

        private void GameScreen_Unloaded(object sender, RoutedEventArgs e)
        {
            StopDescendantAnimations();
            this.CommandBindings.Clear();
            this.InputBindings.Clear();
            ClientEvents.ChatMessageReceived.Unsubscribe(OnChatMessageReceived);
            ClientEvents.TurnEnded.Unsubscribe(OnTurnEnded);
            ClientEvents.TurnStarted.Unsubscribe(OnTurnStarted);
        }

        private void OnChatMessageReceived(DataEventArgs<ChatMessage> args)
        {
            var chatMessage = args.Value;
            if (chatMessage != null)
                ProcessChatMessage(chatMessage);
        }

        private void GameScreen_Loaded(object sender, RoutedEventArgs e)
        {
            ClientEvents.ChatMessageReceived.Subscribe(OnChatMessageReceived);
            ClientEvents.TurnEnded.Subscribe(OnTurnEnded, ThreadOption.UIThread);
            ClientEvents.TurnStarted.Subscribe(OnTurnStarted, ThreadOption.UIThread);
            RefreshScreen();
        }

        private void OnTurnStarted(ClientEventArgs obj)
        {
            ResumeAnimations();
        }

        private void OnTurnEnded(ClientEventArgs obj)
        {
            PauseAnimations();
        }

        private void ProcessChatMessage(ChatMessage message)
        {
            if (!IsVisible || !this.ClientContext.IsGameInPlay)
                return;
            if (ReferenceEquals(message.Sender, this.ClientContext.LocalPlayer))
                return;
            try
            {
                using (var soundPlayer = new SoundPlayer("Resources/SoundFX/ChatMessage.wav"))
                {
                    soundPlayer.Play();
                }
            }
            catch {}
            RaiseEvent(new RoutedEventArgs(ChatMessageReceivedEvent, this));
        }

        private void DebugShowMapCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var playerEmpire = this.ClientContext.LocalPlayerEmpire;
            if (playerEmpire == null)
                return;

            var game = this.ClientContext.CurrentGame;
            if (game == null)
                return;

            var map = game.Universe.Map;
            var playerCiv = playerEmpire.Civilization;
            var mapData = playerEmpire.MapData;

            for (int x = 0; x < map.Width; x++)
            {
                for (int y = 0; y < map.Height; y++)
                {
                    mapData.SetExplored(new MapLocation(x, y), true);
                }
            }

            foreach (var civ in game.Civilizations)
            {
                if (civ == playerCiv)
                    continue;
                if (game.DiplomacyStatus[playerCiv, civ] == DiplomacyStatus.NoContact)
                    game.DiplomacyStatus[playerCiv, civ] = DiplomacyStatus.Neutral;
            }

            RefreshScreen();
        }

        private void DebugAddCreditsCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var playerEmpire = this.ClientContext.LocalPlayerEmpire;
            if (playerEmpire != null)
                playerEmpire.Credits.AdjustCurrent(10000);
        }

        protected internal virtual void OnClosing(object sender, CancelEventArgs e) {}

        public virtual void RefreshScreen() {}

        #region Implementation of IActiveAware
        public event EventHandler IsActiveChanged;

        private void OnIsActiveChanged()
        {
            var handler = this.IsActiveChanged;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        public bool IsActive
        {
            get { return _isActive; }
            set
            {
                if (Equals(_isActive, value))
                    return;
                _isActive = value;
                OnIsActiveChanged();
            }
        }
        #endregion

        #region Implementation of IGameScreenView
        public TPresentationModel Model { get; set; }

        public virtual void OnCreated() {}
        public virtual void OnDestroyed() {}
        #endregion
    }

    public class PriorityGameScreen<TPresentationModel> : GameScreen<TPresentationModel>
    {
        public PriorityGameScreen([NotNull] IUnityContainer container) : base(container) {}
    }
}
